﻿#include "Plc.h"


Plc::Plc(QString ip)
{
	_ip = ip.toLatin1();
	_ip_ptr = _ip.data();

	_connected = false;
	_client = new TS7Client();

	_sem_queue = new QSemaphore(1);

	//定时器

	//定时器：检查PLC连接	
	tmr_check_plc.setInterval(3000);
	tmr_check_plc.start();
	QObject::connect(&tmr_check_plc, &QTimer::timeout, this, &Plc::connect_plc);

	//队列：定时读plc	
	tmr_read_plc.setInterval(100);
	tmr_read_plc.start();
	QObject::connect(&tmr_read_plc, &QTimer::timeout, this, [=]() {
		Plc_data d = { 0.0f,QString("read"),1 };
		//emit signal_consumer(d);
		to_queue(d);

		});

	//定时器：定时消费
	tmr_comsumer.setInterval(20);
	connect(&tmr_comsumer, &QTimer::timeout, this, [=]() {process();});

	//线程内定时器信号
	tmr_comsumer.start();


	//信号
	//yml变量
	_fs.open("db_vars.yml", FileStorage::READ);

	_pos = parse_var("car_pos");
	_pos_actual = parse_var("car_pos_actual");
	_car_move = parse_var("car_move");
	_car_direction = parse_var("car_direction");
}

Plc::~Plc()
{
	if (_client != nullptr) {
		delete _client;
	}

	emit& QThread::finished;

}

//槽：连接plc
void Plc::connect_to_plc()
{
	if (!is_connected())
		connect_plc();
}

//从PLC读
void Plc::read()
{
	int code; //return code of read plc

	//actual pos
	//"DB_CZ_Param".CAR_Position DB9.DBD48 浮点
	if (_connected) {
		byte data[256];
		//code = _client->DBRead(9, 48, 4, &data);  
		code = _client->ReadArea(_pos_actual.Area, _pos_actual.DBNumber, _pos_actual.Start, _pos_actual.Amount, _pos_actual.WordLen, &data);

		QByteArray qdata(4, 0);
		float value = qFromBigEndian<float>(&data);

		QString s = QString("%1").arg(value);
		emit data_readed(s);
	}
	//motor start
	//db190.dbx2.2
	//int start = 2 * 8 + 2;
	if (_connected) {
		byte data_2[1];
		//_client->ReadArea(S7AreaMK, 190, start, 1, S7WLBit, &data_2);
		//电机运行
		code = _client->ReadArea(_car_move.Area, _car_move.DBNumber, _car_move.Start, _car_move.Amount, _car_move.WordLen, &data_2);
		bool state = data_2[0];
		emit motor_stae(state);

		//移动方向
		code = _client->ReadArea(_car_direction.Area, _car_direction.DBNumber, _car_direction.Start, _car_direction.Amount, _car_direction.WordLen, &data_2);
		state = data_2[0];
		emit car_direction(state);		
		
	}
}

//消费
void Plc::consumer()
{
	if (_queue.isEmpty()) return;

	if (_sem_queue->tryAcquire(1, 3)) {
		Plc_data data = _queue.dequeue();

		//写位置
		if (data.id.compare("dm") == 0) {
			//qDebug() << data.id << Qt::endl;
			write_pos(data.v);
		}
		//读PLC变量
		if (data.id.compare("read") == 0) {
			read();
		}

		_sem_queue->release(1);
	}
}

//写位置到PLC
int Plc::write_pos(float value)
{
	if (!is_connected()) return -1;

	//写入1个值
	QByteArray qdata(4, 0);
	qToBigEndian(value, qdata.data());

	byte data[256];
	int len = qdata.length();

	for (int i = 0;i < len;i++) {
		data[i] = qdata.at(i);
	}
	//db1060.dbd0
	//int code = _client->WriteArea(S7AreaDB, 1060, 0, 1, S7WLReal, &data);
	int code = _client->WriteArea(
		_pos_actual.Area,
		_pos_actual.DBNumber,
		_pos_actual.Start,
		_pos_actual.Amount,
		_pos_actual.WordLen,
		&data
	);
	return code;
}

//写开关照明到PLC
int Plc::lamp(bool on_or_off)
{
	byte data[1];

	//db1060.dbx12.2
	int start = 12 * 8 + 2;
	_client->WriteArea(S7AreaMK, 1060, start, 1, S7WLBit, &data);
	return 0;
}

void Plc::connect_plc()
{
	if (_client != NULL && !_client->Connected()) {
		//const char* ip = "127.0.0.1";

		int rack = 0;
		int slot = 3;
		//const char* ip = _ip.toLatin1().data();
		//qDebug() << "plc ip" << QString(_ip_ptr) << Qt::endl;

		_client->ConnectTo(_ip_ptr, rack, slot);
		_connected = _client->Connected();

	}

	emit plc_state(_connected);
}

//解析变量
DBVar Plc::parse_var(std::string s)
{
	DBVar var{};

	FileNode node = _fs[s];
	std::string addr = node["addr"];
	std::string type = node["type"];
	int len = node["len"];

	std::string pattern = "DB([0-9]+).DB[D|X]?([0-9]+).?([0-9]*)";
	std::regex re(pattern);
	std::smatch matches;
	bool found = std::regex_search(addr, matches, re);
	if (found) {
		try {
			var.DBNumber = std::stoi(matches[1]);
			var.Start = std::stoi(matches[2]);

			if (matches.size() == 4 && matches[3] != "") {
				int b = std::stoi(matches[3]);
				var.Start = var.Start * 8 + b;
			}
		}
		catch (std::invalid_argument) {
			qDebug() << "stoi failure" << Qt::endl;
		}
	}

	if (type == "float") {
		var.Area = S7AreaDB;
		var.Amount = len;
		var.WordLen = S7WLReal;
	}
	if (type == "bool") {
		var.Area = S7AreaDB;
		var.Amount = len;
		var.WordLen = S7WLBit;
	}


	return var;
}

bool Plc::is_connected()
{
	//bool result = false;
	//if (_client != nullptr)
	//	result = _client->Connected();

	return _connected;
}

//数据入队列
void Plc::to_queue(Plc_data data)
{
	//队列不能太满
	if (_queue.size() > 100) {
		return;
	}
	_sem_queue->tryAcquire(1, data.timeout);
	_queue.enqueue(data);
	_sem_queue->release(1);
}


//线程
void Plc::process() {
	consumer();
	//QThread::msleep(100);
	//qDebug() << "slot:" << QThread::currentThreadId() << Qt::endl;

}